#include "stdafx.h"
#include "TerrainPart.h"

namespace MyGame {

TerrainPart::TerrainPart(Game* game) : DrawableGameComponent(game) {
    this->indices = NULL;
    this->mesh = NULL;
    this->vertices = NULL;
    this->waterHeight = 0;
}

TerrainPart::~TerrainPart(void) {
}

ID3DXMesh* TerrainPart::GetMesh() const {
    return this->mesh;
}

void TerrainPart::SetHeightMap(HeightMap* val) {
    this->heightMap = val;
}

void TerrainPart::SetVertices(vector<Vertex>* val) {
    this->vertices = val;
}

void TerrainPart::SetWaterHeight(FLOAT val) {
    this->waterHeight = val;
}

void TerrainPart::GenerateIndices() {
    int length = this->heightMap->GetLength();
    int width = this->heightMap->GetWidth();
    int indiceCount = (length - 1) * (width - 1) * 6;
    this->indices = new vector<DWORD>(indiceCount);
    int i = 0;
    for (int z = 0; z < length - 1; z++) {
        for (int x = 0; x < width - 1; x++) {
            DWORD vertexIndex = x + z * width;
            (*this->indices)[i++] = vertexIndex;
            (*this->indices)[i++] = vertexIndex + width;
            (*this->indices)[i++] = vertexIndex + width + 1;
            (*this->indices)[i++] = vertexIndex;
            (*this->indices)[i++] = vertexIndex + width + 1;
            (*this->indices)[i++] = vertexIndex + 1;
        }
    }
}

void TerrainPart::GenerateVertices() {
    int alongLength = heightMap->GetLength();
    int alongWidth = heightMap->GetWidth();
    this->vertices = new vector<Vertex>(alongLength * alongWidth);
    FLOAT** map = heightMap->GetMap();
    for (int x = 0; x < alongWidth; x++) {
        for (int z = 0; z < alongLength; z++) {
            (*this->vertices)[z * alongWidth + x] = Vertex(
                                                        (float)x + offsetX,
                                                        (float)map[x][z],
                                                        (float)z + offsetZ,
                                                        0, 1, 0,
                                                        (float)x / (alongWidth - 1),
                                                        (float)z / (alongLength - 1));
        }
    }
}

void TerrainPart::InitializeMesh(FLOAT offsetX, FLOAT offsetZ) {
    this->offsetX = offsetX;
    this->offsetZ = offsetZ;
    if (this->vertices == NULL) {
        this->GenerateVertices();
    }

    this->GenerateIndices();

    int numFaces = (this->heightMap->GetLength() - 1) * (this->heightMap->GetWidth() - 1) * 2;
    int numVertices = this->heightMap->GetLength() * this->heightMap->GetWidth();
    D3DXCreateMeshFVF(
        numFaces,
        numVertices,
        D3DXMESH_MANAGED | D3DXMESH_32BIT,
        Vertex::FVF,
        this->GetGame()->GetGraphicsDevice(),
        &this->mesh);

    Vertex* vertices = NULL;
    this->mesh->LockVertexBuffer(0, (void**)&vertices);
    memcpy(vertices, &(*this->vertices)[0], sizeof(Vertex) * this->vertices->size());
    this->mesh->UnlockVertexBuffer();

    DWORD* indices = 0;
    this->mesh->LockIndexBuffer(0, (void**)&indices);
    memcpy(indices, &(*this->indices)[0], sizeof(DWORD) * this->indices->size());
    this->mesh->UnlockVertexBuffer();

    DWORD* attributeBuffer = 0;
    this->mesh->LockAttributeBuffer(0, &attributeBuffer);
    int i = 0;
    for (vector<DWORD>::iterator it = this->indices->begin(); it != this->indices->end(); ++it) {
        Vertex v1 = (*this->vertices)[*it++];
        Vertex v2 = (*this->vertices)[*it++];
        Vertex v3 = (*this->vertices)[*it];
        if  (v1.y < this->waterHeight && v2.y < this->waterHeight && v3.y < this->waterHeight) {
            attributeBuffer[i] = 1;
        } else {
            attributeBuffer[i] = 0;
        }

        i++;
    }

    this->mesh->UnlockAttributeBuffer();

    DWORD* adjacencyBuffer = new DWORD[this->mesh->GetNumFaces() * 3];
    HRESULT hr = this->mesh->GenerateAdjacency(0.0f, adjacencyBuffer);
    this->mesh->OptimizeInplace(
        D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE,
        adjacencyBuffer,
        NULL, NULL, NULL);

    d3d::Delete(this->indices);
    d3d::Delete(this->vertices);
}

}